﻿// Decompiled with JetBrains decompiler
// Type: TaleWorlds.MountAndBlade.View.MissionViews.ReplayCaptureLogic
// Assembly: TaleWorlds.MountAndBlade.View, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: 61152EB8-513B-4D4A-8AC0-0D1465C6CF6C
// Assembly location: D:\steam\steamapps\common\Mount & Blade II Bannerlord\Modules\Native\bin\Win64_Shipping_Client\TaleWorlds.MountAndBlade.View.dll

using System;
using System.Collections.Generic;
using System.Linq;
using TaleWorlds.DotNet;
using TaleWorlds.Engine;
using TaleWorlds.Library;

#nullable disable
namespace TaleWorlds.MountAndBlade.View.MissionViews
{
  public class ReplayCaptureLogic : MissionView
  {
    private ReplayMissionView _replayLogic;
    private bool _renderActive;
    public const float CaptureFrameRate = 60f;
    private float _replayTimeDiff;
    private bool _frameSkip;
    private Path _path;
    private PlatformDirectoryPath _directoryPath;
    private bool _saveScreenshots;
    private readonly KeyValuePair<float, MatrixFrame> _invalid = new KeyValuePair<float, MatrixFrame>(-1f, new MatrixFrame());
    private SortedDictionary<float, SortedDictionary<int, MatrixFrame>> _cameraKeys;
    private bool _isRendered;
    private int _lastUsedIndex;
    private int _ssNum;

    private void CheckFixedDeltaTimeMode()
    {
      if (this.RenderActive && this.SaveScreenshots)
      {
        this.Mission.FixedDeltaTime = 0.0166666675f;
        this.Mission.FixedDeltaTimeMode = true;
      }
      else
      {
        this.Mission.FixedDeltaTime = 0.0f;
        this.Mission.FixedDeltaTimeMode = false;
      }
    }

    private bool RenderActive
    {
      get => this._renderActive;
      set
      {
        this._renderActive = value;
        this.CheckFixedDeltaTimeMode();
      }
    }

    private Camera MissionCamera
    {
      get
      {
        return this.MissionScreen == null || !((NativeObject) this.MissionScreen.CombatCamera != (NativeObject) null) ? (Camera) null : this.MissionScreen.CombatCamera;
      }
    }

    private float ReplayTime => this.Mission.CurrentTime - this._replayTimeDiff;

    private bool SaveScreenshots
    {
      get => this._saveScreenshots;
      set
      {
        this._saveScreenshots = value;
        this.CheckFixedDeltaTimeMode();
      }
    }

    private KeyValuePair<float, MatrixFrame> PreviousKey => this.GetPreviousKey();

    private KeyValuePair<float, MatrixFrame> NextKey => this.GetNextKey();

    private KeyValuePair<float, MatrixFrame> GetPreviousKey()
    {
      KeyValuePair<float, MatrixFrame> previousKey = this._invalid;
      if (!this._cameraKeys.Any<KeyValuePair<float, SortedDictionary<int, MatrixFrame>>>())
        return previousKey;
      foreach (KeyValuePair<float, SortedDictionary<int, MatrixFrame>> cameraKey in this._cameraKeys)
      {
        if ((double) cameraKey.Key <= (double) this.ReplayTime)
          previousKey = new KeyValuePair<float, MatrixFrame>(cameraKey.Key, cameraKey.Value[cameraKey.Value.Count - 1]);
      }
      return previousKey;
    }

    private KeyValuePair<float, MatrixFrame> GetNextKey()
    {
      KeyValuePair<float, MatrixFrame> nextKey = this._invalid;
      if (!this._cameraKeys.Any<KeyValuePair<float, SortedDictionary<int, MatrixFrame>>>())
        return nextKey;
      foreach (KeyValuePair<float, SortedDictionary<int, MatrixFrame>> cameraKey in this._cameraKeys)
      {
        if ((double) cameraKey.Key > (double) this.ReplayTime)
        {
          nextKey = new KeyValuePair<float, MatrixFrame>(cameraKey.Key, cameraKey.Value[0]);
          break;
        }
      }
      return nextKey;
    }

    public ReplayCaptureLogic()
    {
      this._cameraKeys = new SortedDictionary<float, SortedDictionary<int, MatrixFrame>>();
    }

    public override void OnBehaviorInitialize()
    {
      base.OnBehaviorInitialize();
      this._replayLogic = this.Mission.GetMissionBehavior<ReplayMissionView>();
      this._replayLogic.OverrideInput(true);
      if (MBCommon.IsPaused)
        return;
      this._replayLogic.Pause();
    }

    public override void OnMissionTick(float dt)
    {
      base.OnMissionTick(dt);
      if (this._frameSkip && !MBCommon.IsPaused)
      {
        if (!this._isRendered)
        {
          this._isRendered = true;
          return;
        }
        this._replayLogic.Pause();
        this._frameSkip = false;
      }
      if (!this.RenderActive)
        return;
      this.SaveScreenshot();
      if (!this.Mission.Recorder.IsEndOfRecord())
      {
        KeyValuePair<float, MatrixFrame> previousKey = this.PreviousKey;
        KeyValuePair<float, MatrixFrame> nextKey = this.NextKey;
        this._replayLogic.Resume();
        if ((double) nextKey.Key >= 0.0)
        {
          for (int index = 0; index < this._cameraKeys.Count; ++index)
          {
            if ((double) previousKey.Key == (double) this._cameraKeys.ElementAt<KeyValuePair<float, SortedDictionary<int, MatrixFrame>>>(index).Key)
            {
              float num1 = nextKey.Key - previousKey.Key;
              float phase = (this.ReplayTime - previousKey.Key) / num1;
              int count = this._cameraKeys[previousKey.Key].Count;
              MatrixFrame matrixFrame;
              if (this._lastUsedIndex != index && count > 1)
              {
                matrixFrame = this._cameraKeys[previousKey.Key][count - 1];
              }
              else
              {
                matrixFrame = new MatrixFrame()
                {
                  origin = this._path.GetHermiteFrameForDt(phase, index).origin
                };
                Vec3 vec3_1 = previousKey.Value.rotation.s * (1f - phase) + nextKey.Value.rotation.s * phase;
                Vec3 vec3_2 = previousKey.Value.rotation.u * (1f - phase) + nextKey.Value.rotation.u * phase;
                Vec3 vec3_3 = previousKey.Value.rotation.f * (1f - phase) + nextKey.Value.rotation.f * phase;
                matrixFrame.rotation.s = vec3_1;
                matrixFrame.rotation.u = vec3_2;
                matrixFrame.rotation.f = vec3_3;
              }
              double num2 = (double) matrixFrame.rotation.s.Normalize();
              double num3 = (double) matrixFrame.rotation.u.Normalize();
              double num4 = (double) matrixFrame.rotation.f.Normalize();
              matrixFrame.rotation.Orthonormalize();
              this.MissionScreen.CustomCamera.Frame = matrixFrame;
              this._lastUsedIndex = index;
              break;
            }
          }
        }
        else
        {
          if ((double) previousKey.Key < 0.0)
            return;
          int count = this._cameraKeys[previousKey.Key].Count;
          if (count <= 1)
            return;
          MatrixFrame matrixFrame = this._cameraKeys[previousKey.Key][count - 1];
          double num5 = (double) matrixFrame.rotation.s.Normalize();
          double num6 = (double) matrixFrame.rotation.u.Normalize();
          double num7 = (double) matrixFrame.rotation.f.Normalize();
          matrixFrame.rotation.Orthonormalize();
          this.MissionScreen.CustomCamera.Frame = matrixFrame;
        }
      }
      else
      {
        MBDebug.Print("All images are saved.", color: Debug.DebugColor.DarkCyan, debugFilter: 64UL);
        this.RenderActive = false;
        this._replayLogic.ResetReplay();
        this._replayTimeDiff = this.Mission.CurrentTime;
        this.MissionScreen.CustomCamera = (Camera) null;
        this._replayLogic.Pause();
        this.SaveScreenshots = false;
        this._ssNum = 0;
      }
    }

    private void InsertCamKey()
    {
      float replayTime = this.ReplayTime;
      MatrixFrame frame = this.MissionCamera.Frame;
      int key = 0;
      if (this._cameraKeys.ContainsKey(replayTime))
      {
        key = this._cameraKeys[replayTime].Count;
        this._cameraKeys[replayTime].Add(key, frame);
      }
      else
        this._cameraKeys.Add(replayTime, new SortedDictionary<int, MatrixFrame>()
        {
          {
            key,
            frame
          }
        });
      MBDebug.Print("Keyframe to \"" + (object) replayTime + "\" has been inserted with the index: " + (object) key + ".\n", color: Debug.DebugColor.Green, debugFilter: 64UL);
    }

    private void MoveToNextFrame()
    {
      this._replayLogic.FastForward(0.0166666675f);
      this._replayLogic.Resume();
      this._frameSkip = true;
    }

    private void GoToKey(float keyTime)
    {
      if ((double) keyTime < 0.0 || !this._cameraKeys.ContainsKey(keyTime) || (double) keyTime == (double) this.ReplayTime)
        return;
      MatrixFrame matrixFrame;
      if ((double) keyTime < (double) this.ReplayTime)
      {
        matrixFrame = this._cameraKeys[keyTime][this._cameraKeys[keyTime].Count - 1];
        this._replayLogic.Rewind(this.ReplayTime - keyTime);
        this._replayTimeDiff = this.Mission.CurrentTime;
      }
      else
      {
        matrixFrame = this._cameraKeys[keyTime][0];
        this._replayLogic.FastForward(keyTime - this.ReplayTime);
      }
      this.MissionCamera.Frame = matrixFrame;
    }

    private void SetPath()
    {
      if ((NativeObject) this.Mission.Scene.GetPathWithName("CameraPath") != (NativeObject) null)
        this.Mission.Scene.DeletePathWithName("CameraPath");
      this.Mission.Scene.AddPath("CameraPath");
      foreach (KeyValuePair<float, SortedDictionary<int, MatrixFrame>> cameraKey in this._cameraKeys)
        this.Mission.Scene.AddPathPoint("CameraPath", cameraKey.Value[0]);
      this._path = this.Mission.Scene.GetPathWithName("CameraPath");
    }

    private void Render(bool saveScreenshots = false)
    {
      if (!this._cameraKeys.ContainsKey(0.0f))
        this._cameraKeys.Add(0.0f, new SortedDictionary<int, MatrixFrame>()
        {
          {
            0,
            this.MissionCamera.Frame
          }
        });
      else
        this._cameraKeys[0.0f] = new SortedDictionary<int, MatrixFrame>()
        {
          {
            0,
            this.MissionCamera.Frame
          }
        };
      this._replayLogic.ResetReplay();
      this._replayLogic.Pause();
      this._replayTimeDiff = this.Mission.CurrentTime;
      this.SetPath();
      this.SaveScreenshots = saveScreenshots;
      this.RenderActive = true;
      this._lastUsedIndex = 0;
      this.MissionScreen.CustomCamera = this.MissionScreen.CombatCamera;
    }

    private void SaveScreenshot()
    {
      if (!this.SaveScreenshots)
        return;
      if (string.IsNullOrEmpty(this._directoryPath.Path))
        this._directoryPath = new PlatformDirectoryPath(PlatformFileType.User, "Captures") + ("Cap_" + string.Format("{0:yyyy-MM-dd_hh-mm-ss-tt}", (object) DateTime.Now));
      Utilities.TakeScreenshot(new PlatformFilePath(this._directoryPath, "time_" + string.Format("{0:000000}", (object) this._ssNum) + ".bmp"));
      ++this._ssNum;
    }
  }
}
